
//
// 101124 - AH - (Garage) Created.
// 101125a- AH - First implementation finished with only Mine as root and
//               resources as its children, do attributes later.
// 101125b- AH - EMPTY_MODEL.
// 101125c- AH - getMine().
// 101127 - AH - Shows tags.
// 101217 - AH - Resources are now of type Resource, not AttributeObject 
//               directly.
// 110604 - AH - (Bybanen) Changes related to that tags are no longer String
//               but SoekratName objects.
// 110605 - AH - (Garage) Changes related to that resource name is now a 
//               SoekrateName.
// 110615a- AH - Now extends AbstractTreeModel.
// 110615b- AH - Now that tree is editable implemented 
//               valueForPathChanged(TreePath, Object).
// 110617a- AH - createResource().
// 110617b- AH - createTag(Resource).
// 110617c- AH - removeResource(Resource).
// 110617d- AH - removeTag(Resource, SoekratName).
// 110708 - AH - (Special) Changes related to correctly populate and change 
//               tags.
// 110808 - AH - (Bybanen *) Uses Mine.(Resource, SoekratName) and not remove
//               tags directly from resource Tags object which causes Mine to
//               still have references to it.
//

package org.soekrat.gui.swing.resource;

import javax.swing.JTree;
import javax.swing.event.TreeModelEvent;
import javax.swing.tree.TreePath;

import org.soekrat.engine.SoekratName;
import org.soekrat.engine.mine.Mine;
import org.soekrat.engine.mine.Resource;
import org.soekrat.engine.mine.Tags;
import org.soekrat.gui.swing.util.AbstractTreeModel;

/**
 * <p>
 *   Adapts a
 *   {@link Mine} to a
 *   {@link JTree} view.
 * </p>
 * @author Arne Halvorsen (AH)
 * @author Laetitia Le Chatton (LLC) 
 */
public final class ResourceTreeModel extends AbstractTreeModel 
{
  /**
   * <p>
   *   The shared empty model.
   * </p>
   */
  public static final ResourceTreeModel EMPTY_MODEL=new ResourceTreeModel();
  
  private final Mine m_mine;
  
  /**
   * <p>
   *   Creates the single empty model.
   * </p>
   */
  private ResourceTreeModel(){ m_mine=null; }
  
  /**
   * <p>
   *   Constructor.
   * </p>
   * @param mine {@link Mine} to adapt.
   */
  public ResourceTreeModel(Mine mine)
  {
    if (mine==null)
    {
      throw new NullPointerException("mine");
    }
    
    m_mine=mine;
  }
  
  /**
   * <p>
   *   Gets
   *   {@link Mine} {@code this} adapts.
   * </p>
   * @return {@code Mine} or {@code null} if {@code this} is the 
   *         {@link #EMPTY_MODEL empty model}.
   */
  public Mine getMine(){ return m_mine; }

  // Specified in TreeModel.
  public Object getChild(Object parent, int idx) 
  {
    if (parent==m_mine)
    {
      return m_mine.getResource(idx);
    }
    else if (parent instanceof Resource)
    {
      Resource r=(Resource)parent;
      Tags tags=(Tags)r.getAttribute("tags");
      if (tags==null)
      {
        throw new IndexOutOfBoundsException(Integer.toString(idx));
      }
      SoekratName t=tags.getTag(idx);
      return t;
    }
    
    throw new Error();
  }

  // Specified in TreeModel.
  public int getChildCount(Object parent)
  {
    if (parent==m_mine)
    {
      return m_mine.size();
    }
    else if (parent instanceof Resource)
    {
      Resource r=(Resource)parent;
      Tags tags=(Tags)r.getAttribute("tags");
      return tags==null ? 0 : tags.size();
    }
    else if (parent instanceof String)
    {
      return 0;
    }
    
    throw new Error();
  }

  // Specified in TreeModel.
  public int getIndexOfChild(Object parent, Object child) 
  {
    if (parent==m_mine)
    {
      return m_mine.getIndexOfResource((Resource)child);
    }
    else if (parent instanceof Resource)
    {
      Resource r=(Resource)parent;
      Tags tags=(Tags)r.getAttribute("tags");
      if (tags==null) return -1;
      SoekratName tag=(SoekratName)child;
      return tags.getIndexOf(tag);
    }
    
    throw new Error();
  }

  // Specified in TreeModel.
  public Object getRoot(){ return m_mine; }

  // Specified in TreeModel.
  public boolean isLeaf(Object node)
  {
    if (node==m_mine)
    {
      return false;
    }
    else if (node instanceof Resource)
    {
      Resource r=(Resource)node;
      Tags tags=(Tags)r.getAttribute("tags");
      return tags==null || tags.isEmpty();
    }
    else if (node instanceof SoekratName)
    {
      return true;
    }
    
    throw new Error("Uknown type: "+node.getClass().toString());
  }
  
  // Specified in TreeModel.
  public void valueForPathChanged(TreePath path, Object val)
  {
    String txt=(val==null) ? "" : val.toString();
    txt=(txt==null) ? "" : txt.trim();
    if (txt.length()==0) return;
    
    Object last=path.getLastPathComponent();
    
    TreeModelEvent e=null;
    
    if (last instanceof Resource)
    {
      if (m_mine.getNamed(txt)==null)
      {
        Resource r=(Resource)last;
        r.setSoekratName(txt);
        int idx=m_mine.getIndexOfResource(r);
        e=new TreeModelEvent(this, new Object[]{ m_mine }, new int[]{ idx }, 
          new Object[]{ r });
      }
    }
    else if (last instanceof SoekratName)
    {
      SoekratName sname=(SoekratName)last;
      Resource r=(Resource)path.getParentPath().getLastPathComponent();
      Tags tags=(Tags)r.getAttribute("tags");
      int idx=tags.getIndexOf(sname);
      if (idx!=-1)
      {
        //sname.set(txt);
        m_mine.renameTag(sname, txt);
        e=new TreeModelEvent(this, new Object[]{ m_mine, r }, new int[]{ idx },
          new Object[]{ sname });
      }
    }
    
    if (e!=null) fire(e, EventType.CHANGED);
  }
  
  /**
   * <p>
   *   Creates
   *   {@link Resource} with initial name.
   * </p>
   * @return Created.
   */
  public Resource createResource()
  {
    Resource retVal=m_mine.createResource();
    
    //System.out.println("created resource: "+retVal.getName());
    
    int idx=m_mine.getIndexOfResource(retVal);
    
    TreeModelEvent e=new TreeModelEvent(this, new Object[]{ m_mine }, 
      new int[]{ idx }, new Object[]{ retVal });
    
    fire(e, EventType.INSERTED);
    
    return retVal;
  }
  
  /**
   * <p>
   *   Remove
   *   {@link Resource}.
   * </p>
   * @param r Resource to remove.
   * @throws IllegalArgumentException If {@code r} is not
   *         {@link Resource} in adapted
   *         {@link Mine}. 
   */
  public void removeResource(Resource r)
  {
    if (r==null)
    {
      throw new NullPointerException("r");
    }
    
    int idx=m_mine.getIndexOfResource(r);
    if (idx==-1)
    {
      throw new IllegalArgumentException("r is not in mine");
    }
    
    m_mine.remove(r);
      
    TreeModelEvent e=new TreeModelEvent(this, new Object[]{ m_mine }, 
      new int[]{ idx }, new Object[]{ r });
        
    fire(e, EventType.REMOVED);
  }
  
  /**
   * <p>
   *   Creates tag with initial name.
   * </p>
   * @param r {@code Resource} to create name for.
   * @return Created.
   * @throws IllegalArgumentException If {@code r} is not
   *         {@link Resource} in adapted
   *         {@link Mine}.
   */
  public SoekratName createTag(Resource r)
  {
    if (r==null)
    {
      throw new NullPointerException("r");
    }
    
    int ridx=m_mine.getIndexOfResource(r);
    if (ridx==-1)
    {
      throw new IllegalArgumentException("r not in mine");
    }
    
    Tags tags=(Tags)r.getAttribute("tags");
    if (tags==null)
    {
      tags=new Tags();
      r.setAttribute("tags", tags);
    }
    
    SoekratName retVal=tags.create();
    
    m_mine.add(r, tags);
    
    int idx=tags.getIndexOf(retVal);
    TreeModelEvent e=new TreeModelEvent(this, new Object[]{ m_mine, r }, 
      new int[]{ idx },  new Object[]{ retVal });
    fire(e, EventType.INSERTED);
    
    return retVal;
  }
  
  /**
   * <p>
   *   Removes tag.
   * </p>
   * @param r   {@link Resource} to remove tag of.
   * @param tag Tag to remove.
   * @throws IllegalArgumentException If {@code r} is not in adapted
   *         {@link Mine}.
   * @throws IllegalArgumentException If {@code tag} is not a tag of {@code r}.
   */
  public void removeTag(Resource r, SoekratName tag)
  {
    if (r==null)
    {
      throw new NullPointerException("r");
    }
    if (tag==null)
    {
      throw new NullPointerException("tag");
    }
    
    int ridx=m_mine.getIndexOfResource(r);
    if (ridx==-1)
    {
      throw new IllegalArgumentException("r not in mine");
    }
    
    Tags tags=(Tags)r.getAttribute("tags");
    if (tags==null)
    {
      throw new IllegalArgumentException("r does not have tag: "+tag);
    }
    
    int idx=tags.getIndexOf(tag);
    if (idx==-1)
    {
      throw new IllegalArgumentException("r does not have tag: "+tag);
    }
    
    m_mine.removeTag(r, tag);
    
    TreeModelEvent e=new TreeModelEvent(this, new Object[]{ m_mine, r }, 
      new int[]{ idx }, new Object[]{ tag });
    fire(e, EventType.REMOVED);
  }

}
